﻿using SketchUpAPI.NET.Enums;
using SketchUpAPI.NET.SketchUp.Exception;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace SketchUpAPI.NET.Objects
{
    /// <summary>
    /// 相机
    /// </summary>
    /// <remarks>对 SketchUpAPI 中的 SUCameraRef 的封装</remarks>
    public class Camera : ObjectBase
    {
        #region API 函数
        /// <summary>
        /// 创建一个相机
        /// </summary>
        /// <param name="camera"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraCreate(out IntPtr camera);

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="camera"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraRelease(ref IntPtr camera);

        /// <summary>
        /// 获取相机方向
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="position"></param>
        /// <param name="target"></param>
        /// <param name="up_vector"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetOrientation(IntPtr camera, ref Point3D position, ref Point3D target, ref Vector3D up_vector);

        /// <summary>
        /// 设置相机
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="position"></param>
        /// <param name="target"></param>
        /// <param name="up_vector"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetOrientation(IntPtr camera, Point3D position, Point3D target, Vector3D up_vector);

        /// <summary>
        /// 获取视图变形
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="transformation"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetViewTransformation(IntPtr camera, ref Transformation transformation);

        /// <summary>
        /// 设置透视相机视场角
        /// </summary>
        /// <remarks>如果当前是正交相机，设置之后将会变成透视相机</remarks>
        /// <param name="camera"></param>
        /// <param name="fov"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetPerspectiveFrustumFOV(IntPtr camera, double fov);

        /// <summary>
        /// 获取视场角
        /// </summary>
        /// <remarks>如果当前是正交相机，则返回 <see cref="SUResult.SU_ERROR_NO_DATA"/></remarks>
        /// <param name="camera"></param>
        /// <param name="fov"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetPerspectiveFrustumFOV(IntPtr camera, out double fov);

        /// <summary>
        /// 设置纵横比
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="aspect_ratio"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetAspectRatio(IntPtr camera, double aspect_ratio);

        /// <summary>
        /// 获取纵横比
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="aspect_ratio"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetAspectRatio(IntPtr camera, out double aspect_ratio);

        /// <summary>
        /// 设置正交相机的高度
        /// </summary>
        /// <remarks>如果当前是透视相机，设置该参数后将变成正交相机</remarks>
        /// <param name="camera"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetOrthographicFrustumHeight(IntPtr camera, double height);

        /// <summary>
        /// 获取正交相机的高度
        /// </summary>
        /// <remarks>如果当前是透视相机，则返回 <see cref="SUResult.SU_ERROR_NO_DATA"/></remarks>
        /// <param name="camera"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetOrthographicFrustumHeight(IntPtr camera, out double height);

        /// <summary>
        /// 设置是否是透视相机
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="perspective"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetPerspective(IntPtr camera, bool perspective);

        /// <summary>
        /// 获取是否是透视相机
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="perspective"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetPerspective(IntPtr camera, out bool perspective);

        /// <summary>
        /// 获取裁剪距离
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="znear">近裁剪距离</param>
        /// <param name="zfar">远裁剪距离</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetClippingDistances(IntPtr camera, out double znear, out double zfar);

        /// <summary>
        /// 设置 FOV 是否表示视图高度
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="is_fov_height"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetFOVIsHeight(IntPtr camera, bool is_fov_height);

        /// <summary>
        /// 获取 FOV 是否表示视图高度
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="is_fov_height"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetFOVIsHeight(IntPtr camera, out bool is_fov_height);

        /// <summary>
        /// 为透视相机设置胶片尺寸，用于视场与焦距之间的转换
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="width">宽度（毫米）</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetImageWidth(IntPtr camera, double width);

        /// <summary>
        /// 获取图像宽度
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="width"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetImageWidth(IntPtr camera, out double width);

        /// <summary>
        /// 设置相机描述信息
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="desc"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetDescription(IntPtr camera, [MarshalAs(UnmanagedType.LPUTF8Str)] string desc);

        /// <summary>
        /// 获取相机描述信息
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="desc"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetDescription(IntPtr camera, ref IntPtr desc);

        /// <summary>
        /// 获取相机方向
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="direction"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetDirection(IntPtr camera, ref Vector3D direction);

        /// <summary>
        /// 设置是否是二维相机
        /// </summary>
        /// <remarks>两点透视和照片匹配模式的相机是二维的</remarks>
        /// <param name="camera"></param>
        /// <param name="make_2d"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSet2D(IntPtr camera, bool make_2d);

        /// <summary>
        /// 获取相机是否是二维的
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="is_2d"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGet2D(IntPtr camera, out bool is_2d);

        /// <summary>
        /// 设置相机的二维缩放比
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetScale2D(IntPtr camera, double scale);

        /// <summary>
        /// 获取相机的二维缩放比
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetScale2D(IntPtr camera, out double scale);

        /// <summary>
        /// 设置相机的二维中心
        /// </summary>
        /// <remarks>点坐标在屏幕空间中，由于这是设置二维中心，所以将忽略 Z 坐标</remarks>
        /// <param name="camera"></param>
        /// <param name="center"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraSetCenter2D(IntPtr camera, Point3D center);

        /// <summary>
        /// 获取相机的二维中心
        /// </summary>
        /// <remarks>点坐标在屏幕空间中，由于这是二维中心，所以 Z 坐标始终为 0</remarks>
        /// <param name="camera"></param>
        /// <param name="center"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUCameraGetCenter2D(IntPtr camera, ref Point3D center);
        #endregion

        /// <summary>
        /// 相机描述
        /// </summary>
        public string Description
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetDescription(Id, ref s.Id));
                return s.Dispose();
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetDescription(Id, value));
            }
        }

        /// <summary>
        /// 视图变换
        /// </summary>
        public Transformation Transform
        {
            get
            {
                Transformation t = new Transformation();
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetViewTransformation(Id, ref t));
                return t;
            }
        }

        /// <summary>
        /// 相机图像宽度
        /// </summary>
        /// <remarks>高度可以通过纵横比和宽度综合计算得到</remarks>
        public double Width
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetImageWidth(Id, out double w));
                return w;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetImageWidth(Id, value));
            }
        }

        /// <summary>
        /// 纵横比
        /// </summary>
        public double AspectRatio
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetAspectRatio(Id, out double ratio));
                return ratio;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetAspectRatio(Id, value));
            }
        }

        /// <summary>
        /// 是否是透视相机
        /// </summary>
        public bool IsPerspective
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetPerspective(Id, out bool p));
                return p;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetPerspective(Id, value));
            }
        }

        /// <summary>
        /// 视场角是否表示高度
        /// </summary>
        public bool FOVIsHeight
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetFOVIsHeight(Id, out bool f));
                return f;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetFOVIsHeight(Id, value));
            }
        }

        /// <summary>
        /// 透视FOV
        /// </summary>
        /// <remarks>
        /// 获取值时，如果当前是正交相机，则返回 0；
        /// <para>设置值时，如果当前是正交相机，设置后将变成透视相机</para>
        /// </remarks>
        public double PerspectiveFrustumFOV
        {
            get
            {
                var result = SUCameraGetPerspectiveFrustumFOV(Id, out double fov);
                if (result == SUResult.SU_ERROR_NO_DATA) return 0;
                else SketchUpExceptionHelper.ThrowSUResult(result);

                return fov;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetPerspectiveFrustumFOV(Id, value));
            }
        }

        /// <summary>
        /// 正交相机高度
        /// </summary>
        /// <remarks>
        /// 获取值时：如果当前是透视相机，则返回 0；
        /// <para>设置值时：如果当前是透视相机，则设置后将变成正交相机</para>
        /// </remarks>
        public double OrthographicFrustumHeight
        {
            get
            {
                var result = SUCameraGetOrthographicFrustumHeight(Id, out double height);
                if (result == SUResult.SU_ERROR_NO_DATA) return 0;
                else SketchUpExceptionHelper.ThrowSUResult(result);

                return height;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetOrthographicFrustumHeight(Id, value));
            }
        }

        /// <summary>
        /// 是否是二维相机
        /// </summary>
        /// <remarks>两点透视和照片匹配模式的相机是二维的</remarks>
        public bool Is2D
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGet2D(Id, out bool d));
                return d;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSet2D(Id, value));
            }
        }

        /// <summary>
        /// 二维缩放比
        /// </summary>
        public double Scale2D
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetScale2D(Id, out double scale));
                return scale;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetScale2D(Id, value));
            }
        }

        /// <summary>
        /// 二维中心
        /// </summary>
        /// <remarks>
        /// 设置时：点坐标在屏幕空间中，由于这是设置二维中心，所以将忽略 Z 坐标；
        /// <para>获取时：点坐标在屏幕空间中，由于这是二维中心，所以 Z 坐标始终为 0</para>
        /// </remarks>
        public Point3D Center2D
        {
            get
            {
                Point3D p = new Point3D();
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetCenter2D(Id, ref p));
                return p;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUCameraSetCenter2D(Id, value));
            }
        }

        /// <summary>
        /// 相机方向
        /// </summary>
        public Vector3D Direction
        {
            get
            {
                Vector3D v = new Vector3D();
                SketchUpExceptionHelper.ThrowSUResult(SUCameraGetDirection(Id, ref v));
                return v;
            }
        }

        /// <summary>
        /// 创建一个相机
        /// </summary>
        public Camera()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUCameraCreate(out Id));
        }

        internal Camera(IntPtr id) => Id = id;

        /// <summary>
        /// 获取相机参数
        /// </summary>
        /// <param name="position"></param>
        /// <param name="target"></param>
        /// <param name="up"></param>
        public void GetOrientation(out Point3D position, out Point3D target, out Vector3D up)
        {
            position = new Point3D();
            target = new Point3D();
            up = new Vector3D();
            SketchUpExceptionHelper.ThrowSUResult(SUCameraGetOrientation(Id, ref position, ref target, ref up));
        }

        /// <summary>
        /// 设置相机参数
        /// </summary>
        /// <param name="position"></param>
        /// <param name="target"></param>
        /// <param name="up"></param>
        public void SetOrientation(Point3D position, Point3D target, Vector3D up)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUCameraSetOrientation(Id, position, target, up));
        }

        /// <summary>
        /// 获取裁剪距离
        /// </summary>
        /// <param name="near">近裁剪距离</param>
        /// <param name="far">远裁剪距离</param>
        public void GetClippingDistances(out double near, out double far)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUCameraGetClippingDistances(Id, out near, out far));
        }

        public override void Dispose()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUCameraRelease(ref Id));
        }
    }
}